Skip to content

compare XSRF token in constant time#10327

Open
metsw24-max wants to merge 2 commits into
gwtproject:mainfrom
metsw24-max:constant-time-xsrf-token
Open

compare XSRF token in constant time#10327
metsw24-max wants to merge 2 commits into
gwtproject:mainfrom
metsw24-max:constant-time-xsrf-token

Conversation

@metsw24-max

Copy link
Copy Markdown

validateXsrfToken in XsrfProtectedServiceServlet checks the client-supplied token against the expected value (MD5 hex of the session cookie) with String.equals, which returns on the first mismatching character. The expected value is secret-derived and the check runs on every protected RPC call, so the per-character timing difference is an oracle a remote caller can use to recover the token and defeat the CSRF protection. Switch to MessageDigest.isEqual on the byte arrays so the comparison time no longer depends on how many leading characters match.


if (!expectedToken.equals(xsrfToken.getToken())) {
if (providedToken == null || !MessageDigest.isEqual(
expectedToken.getBytes(), providedToken.getBytes())) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By all rights this should be ASCII, since at least the expectedToken will be from StringUtils.toHexString above, but nothing guarantees the incoming xsrf token will be in that charset. I don't think there's any way that can go wrong, since we know we're limited to [0-9A-F] on the first array to pass to isEqual, but just wanted to call it out in case anyone else spotted a problem there.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Made both getBytes calls explicit UTF-8 so it no longer rides on the platform default charset. Expected token is hex so it is unaffected either way, but this keeps the provided side deterministic too.

@zbynek

zbynek commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

@metsw24-max what tool did you use to find this?

@metsw24-max

Copy link
Copy Markdown
Author

@zbynek no special tool, I was reading through the XSRF servlet and the String.equals on a secret-derived value jumped out as a timing oracle. I do lean on an LLM a bit for sanity-checking, but the find itself was just reading the code.

@niloc132 niloc132 added the ready This PR has been reviewed by a maintainer and is ready for a CI run. label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready This PR has been reviewed by a maintainer and is ready for a CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants